﻿using System;
using System.Collections;
using System.Collections.Generic;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using HYElectricKernel;
using YJKRGeometry.CalculationAnalysis;
using TopologySearch;

namespace HYRElectric.LightningCalculation
{
	public class LevelProfileAnalyzer
	{
		public LevelProfileAnalyzer(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
		}

		public LevelProfiles GetLevelProfiles(Level level, bool upwardFind)
		{
			LevelProfiles levelProfiles = new LevelProfiles(level.Elevation);
			LevelProfiles result;
			try
			{
				List<Element> elementsByElevation = this.GetElementsByElevation(level, this.m_Doc, upwardFind);
				foreach (RevitLinkInstance revitLinkInstance in this.GetAllRevitLinkInstancesInDoc(this.m_Doc))
				{
					Document linkDocument = revitLinkInstance.GetLinkDocument();
					if (linkDocument != null)
					{
						List<Element> elementsByElevation2 = this.GetElementsByElevation(level, linkDocument, upwardFind);
						elementsByElevation.AddRange(elementsByElevation2);
					}
				}
				List<Pole2D> list = new List<Pole2D>();
				foreach (Element element in elementsByElevation)
				{
					foreach (Solid solid in this.GetElementSolids(element))
					{
						List<Pole2D> solidProfile = this.GetSolidProfile(solid);
						list.AddRange(solidProfile);
					}
				}
				List<List<Curve>> fullFloorEdgeLoops = new StructureLoopSearcher(this.m_Revit).GetFullFloorEdgeLoops(list, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside);
				List<List<EdgeInfo>> list2 = new List<List<EdgeInfo>>();
				foreach (List<Curve> list3 in fullFloorEdgeLoops)
				{
					List<EdgeInfo> list4 = new List<EdgeInfo>();
					foreach (Curve curve in list3)
					{
						list4.Add(new EdgeInfo(curve));
					}
					list2.Add(list4);
				}
				foreach (List<EdgeInfo> profile in list2)
				{
					Profile profile2 = new Profile(profile);
					levelProfiles.AddProfile(profile2);
				}
				result = levelProfiles;
			}
			catch (Exception)
			{
				result = levelProfiles;
			}
			return result;
		}

		public LevelProfiles GetOffsetProfiles(LevelProfiles levelProfilses, double offset)
		{
			LevelProfiles levelProfiles = new LevelProfiles(levelProfilses.Elevation);
			new List<Pole2D>();
			foreach (Profile profile in levelProfilses.Profiles)
			{
				Profile profile2 = null;
				if (this.OffsetProfile(profile, offset, out profile2))
				{
					levelProfiles.AddProfile(profile2);
				}
			}
			return levelProfiles;
		}

		public LevelProfiles GetUnionProfiles(LevelProfiles levelProfilses)
		{
			List<Pole2D> list = new List<Pole2D>();
			foreach (Profile profile in levelProfilses.Profiles)
			{
				foreach (EdgeInfo edgeInfo in profile.EdgeLoop)
				{
					Pole2D item = Pole2D.TransformWallToStructure2DPole(edgeInfo.ConvertTo(), ElementId.InvalidElementId, 0.0, 0.0);
					list.Add(item);
				}
			}
			List<List<Curve>> fullFloorEdgeLoops = new StructureLoopSearcher(this.m_Revit).GetFullFloorEdgeLoops(list, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside);
			LevelProfiles levelProfiles = new LevelProfiles(levelProfilses.Elevation);
			foreach (List<Curve> list2 in fullFloorEdgeLoops)
			{
				List<EdgeInfo> list3 = new List<EdgeInfo>();
				foreach (Curve curve in list2)
				{
					list3.Add(new EdgeInfo(curve));
				}
				Profile profile2 = new Profile(list3);
				levelProfiles.AddProfile(profile2);
			}
			return levelProfiles;
		}

		public List<XYZ> CetMinAreaRect(LevelProfiles levelProfilses, ref double length, ref double width)
		{
			List<XYZ> list = new List<XYZ>();
			foreach (Profile profile in levelProfilses.Profiles)
			{
				foreach (EdgeInfo edgeInfo in profile.EdgeLoop)
				{
					IList<XYZ> list2 = edgeInfo.ConvertTo().Tessellate();
					for (int i = 0; i < list2.Count - 1; i++)
					{
						list.Add(list2[i]);
					}
				}
			}
			List<XYZ> result = new List<XYZ>();
			if (list.Count > 2)
			{
				MinAreaBoundingBoxCalculator.MinAreaRect(list, ref result, ref length, ref width);
			}
			return result;
		}

		private bool OffsetProfile(Profile profile, double offset, out Profile offsetProfile)
		{
			List<XYZ> list = new List<XYZ>();
			foreach (EdgeInfo edgeInfo in profile.EdgeLoop)
			{
				list.Add(edgeInfo.StartPoint);
			}
			bool flag = Geometry.IsAntiClockwise(list);
			List<EdgeInfo> list2 = new List<EdgeInfo>();
			foreach (EdgeInfo edgeInfo2 in profile.EdgeLoop)
			{
				ThicknessOfEdgeInfo thicknessOfEdgeInfo = new ThicknessOfEdgeInfo(edgeInfo2.StartPoint, edgeInfo2.EndPoint, edgeInfo2.Bulge, offset * 2.0);
				EdgeInfo item4 = null;
				EdgeInfo item2 = null;
				thicknessOfEdgeInfo.GetDoubleLine(ref item2, ref item4);
				if (!flag)
				{
					if (thicknessOfEdgeInfo.IsArc)
					{
						list2.Add(item2);
					}
					else
					{
						list2.Add(item4);
					}
				}
				else if (thicknessOfEdgeInfo.IsArc)
				{
					list2.Add(item4);
				}
				else
				{
					list2.Add(item2);
				}
			}
			List<EdgeInfo> list3 = new List<EdgeInfo>();
			for (int i = 0; i < list2.Count; i++)
			{
				EdgeInfo edgeInfo3 = list2[i];
				list3.Add(edgeInfo3);
				EdgeInfo edgeInfo4;
				if (i == list2.Count - 1)
				{
					edgeInfo4 = list2[0];
				}
				else
				{
					edgeInfo4 = list2[i + 1];
				}
				if (!this.HasIntersect(edgeInfo3, edgeInfo4))
				{
					XYZ endPoint = profile.EdgeLoop[i].EndPoint;
					double bulge = Geometry.GetBulge(edgeInfo3.EndPoint, edgeInfo4.StartPoint, endPoint, true);
					EdgeInfo item3 = new EdgeInfo(edgeInfo3.EndPoint, edgeInfo4.StartPoint, bulge);
					list3.Add(item3);
				}
			}
			List<Pole2D> list4 = new List<Pole2D>();
			foreach (EdgeInfo edgeInfo5 in list3)
			{
				if (!Geometry.IsEqual(edgeInfo5.StartPoint, edgeInfo5.EndPoint))
				{
					Pole2D pole = null;
					if (edgeInfo5.IsArc)
					{
						pole = Pole2D.NewStructure2DPole(ElementId.InvalidElementId, edgeInfo5.StartPoint, edgeInfo5.EndPoint, edgeInfo5.Center, edgeInfo5.Normal, 0.0, 0.0);
					}
					else
					{
						pole = Pole2D.NewStructure2DPole(ElementId.InvalidElementId, edgeInfo5.StartPoint, edgeInfo5.EndPoint, 0.0, 0.0);
					}
					if (list4.FindIndex((Pole2D item) => pole.SameAs(item)) == -1)
					{
						list4.Add(pole);
					}
				}
			}
			List<EdgeInfo> list5 = new List<EdgeInfo>();
			foreach (List<Curve> list6 in new StructureLoopSearcher(this.m_Revit).GetFullFloorEdgeLoops(list4, StructureLoopSearcher.SearchEdgeLocation.SEL_Outside))
			{
				foreach (Curve curve in list6)
				{
					list5.Add(new EdgeInfo(curve));
				}
			}
			offsetProfile = new Profile(list5);
			return true;
		}

		private bool HasIntersect(EdgeInfo edge1, EdgeInfo edge2)
		{
			if (Geometry.IsEqual(edge1.StartPoint, edge2.StartPoint, 0.0001) || Geometry.IsEqual(edge1.EndPoint, edge2.EndPoint, 0.0001) || Geometry.IsEqual(edge1.EndPoint, edge2.StartPoint, 0.0001) || Geometry.IsEqual(edge1.StartPoint, edge2.EndPoint, 0.0001))
			{
				return true;
			}
			Curve curve = edge1.ConvertTo();
			Curve curve2 = edge2.ConvertTo();
			IntersectionResultArray intersectionResultArray;
			if (curve.Intersect(curve2, out intersectionResultArray) == SetComparisonResult.Overlap)
			{
				IEnumerator enumerator = intersectionResultArray.GetEnumerator();
				{
					if (enumerator.MoveNext())
					{
						IntersectionResult intersectionResult = (IntersectionResult)enumerator.Current;
						return true;
					}
				}
				return false;
			}
			return false;
		}

		public double GetBuildingMaxHeight()
		{
			double num = this.GetBuildingMaxHeightInDoc(this.m_Doc);
			foreach (RevitLinkInstance revitLinkInstance in this.GetAllRevitLinkInstancesInDoc(this.m_Doc))
			{
				Document linkDocument = revitLinkInstance.GetLinkDocument();
				if (linkDocument != null)
				{
					double buildingMaxHeightInDoc = this.GetBuildingMaxHeightInDoc(linkDocument);
					if (Geometry.LessThan(num, buildingMaxHeightInDoc))
					{
						num = buildingMaxHeightInDoc;
					}
				}
			}
			return num;
		}

		private double GetBuildingMaxHeightInDoc(Document doc)
		{
			double num = 0.0;
			new List<Element>();
			foreach (Element element in new FilteredElementCollector(doc).OfClass(typeof(Wall)).ToElements())
			{
				Wall wall = element as Wall;
				if (wall.ParametersMap.Contains("已附着顶部") || wall.ParametersMap.Contains("Top is Attached"))
				{
					double num2 = 0.0;
					double num3 = 0.0;
					this.GetWallElevations(wall, doc, ref num2, ref num3);
					if (Geometry.LessThan(num, num3))
					{
						num = num3;
					}
				}
			}
            foreach (Element column in new FilteredElementCollector(this.m_Doc).OfClass(typeof(FamilyInstance)).OfCategory((BuiltInCategory)(-1999010)).ToElements())
			{
				double num4 = 0.0;
				double num5 = 0.0;
				this.GetColumnElevations(column, doc, ref num4, ref num5);
				if (Geometry.LessThan(num, num5))
				{
					num = num5;
				}
			}
			return num;
		}

		private List<RevitLinkInstance> GetAllRevitLinkInstancesInDoc(Document doc)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc).OfClass(typeof(RevitLinkInstance)).ToElementIds();
			List<RevitLinkInstance> list = new List<RevitLinkInstance>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(RevitLinkInstance)))
				{
					RevitLinkInstance revitLinkInstance = element as RevitLinkInstance;
					if (revitLinkInstance != null)
					{
						list.Add(revitLinkInstance);
					}
				}
			}
			return list;
		}

		private List<Element> GetElementsByElevation(Level level, Document doc, bool upwardFind)
		{
			List<Element> list = new List<Element>();
			if (level == null)
			{
				return list;
			}
			double num = Common.MMToFeet(100.0);
			foreach (Element element in new FilteredElementCollector(doc).OfClass(typeof(Wall)).ToElements())
			{
				Wall wall = element as Wall;
				if (wall.ParametersMap.Contains("已附着顶部") || wall.ParametersMap.Contains("Top is Attached"))
				{
					double num2 = 0.0;
					double num3 = 0.0;
					this.GetWallElevations(wall, doc, ref num2, ref num3);
					if (!upwardFind)
					{
						if (Geometry.Greaterthan_Or_Equal(num2, level.Elevation))
						{
							continue;
						}
						if (Geometry.LessThan(num3 + num, level.Elevation))
						{
							continue;
						}
					}
					else if (Geometry.GreaterThan(num2 - num, level.Elevation) || Geometry.Lessthan_Or_Equal(num3, level.Elevation))
					{
						continue;
					}
					list.Add(wall);
				}
			}
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc);
			foreach (Element element2 in filteredElementCollector.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_Columns).ToElements())
			{
				double num4 = 0.0;
				double num5 = 0.0;
				this.GetColumnElevations(element2, doc, ref num4, ref num5);
				if (!upwardFind)
				{
					if (Geometry.Greaterthan_Or_Equal(num4, level.Elevation))
					{
						continue;
					}
					if (Geometry.LessThan(num5 + num, level.Elevation))
					{
						continue;
					}
				}
				else if (Geometry.GreaterThan(num4 - num, level.Elevation) || Geometry.Lessthan_Or_Equal(num5, level.Elevation))
				{
					continue;
				}
				list.Add(element2);
			}
			new FilteredElementCollector(doc);
			foreach (Element element3 in filteredElementCollector.OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_StructuralColumns).ToElements())
			{
				double num6 = 0.0;
				double num7 = 0.0;
				this.GetColumnElevations(element3, doc, ref num6, ref num7);
				if (!upwardFind)
				{
					if (Geometry.Greaterthan_Or_Equal(num6, level.Elevation))
					{
						continue;
					}
					if (Geometry.LessThan(num7 + num, level.Elevation))
					{
						continue;
					}
				}
				else if (Geometry.GreaterThan(num6 - num, level.Elevation) || Geometry.Lessthan_Or_Equal(num7, level.Elevation))
				{
					continue;
				}
				list.Add(element3);
			}
			return list;
		}

		private void GetWallElevations(Wall wall, Document doc, ref double bottom, ref double top)
		{
			try
			{
				ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
				Level level = doc.GetElement(elementId) as Level;
				bottom = level.Elevation;
				double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
				bottom += num;
				ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
				if (elementId2 == ElementId.InvalidElementId)
				{
					double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
					top = bottom + num2;
				}
				else
				{
					Level level2 = doc.GetElement(elementId2) as Level;
					top = level2.Elevation;
					double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
					top += num3;
				}
			}
			catch (Exception)
			{
			}
		}

		private void GetColumnElevations(Element column, Document doc, ref double bottom, ref double top)
		{
			try
			{
				Parameter parameter = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM);
				if (parameter != null)
				{
					ElementId elementId = parameter.AsElementId();
					Level level = doc.GetElement(elementId) as Level;
					bottom = level.Elevation;
				}
				double num = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).AsDouble();
				bottom += num;
				Parameter parameter2 = column.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM);
				if (parameter2 != null)
				{
					ElementId elementId2 = parameter2.AsElementId();
					Level level2 = doc.GetElement(elementId2) as Level;
					top = level2.Elevation;
				}
				double num2 = column.get_Parameter(BuiltInParameter.SCHEDULE_TOP_LEVEL_OFFSET_PARAM).AsDouble();
				top += num2;
			}
			catch (Exception)
			{
			}
		}

		private List<Solid> GetElementSolids(Element element)
		{
			Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
            options.DetailLevel = (ViewDetailLevel)3;
			options.IncludeNonVisibleObjects = true;
			GeometryElement geometryElement;
			if (element.GetType() == typeof(FamilyInstance))
			{
				FamilyInstance familyInstance = element as FamilyInstance;
				geometryElement = familyInstance.GetOriginalGeometry(options);
				geometryElement = geometryElement.GetTransformed(familyInstance.GetTransform());
			}
			else
			{
				geometryElement = element.get_Geometry(options);
			}
			List<Solid> list = new List<Solid>();
			if (null == geometryElement)
			{
				return list;
			}
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (!(geometryObject.GetType() != typeof(Solid)))
				{
					Solid solid = geometryObject as Solid;
					if (solid != null && !Geometry.LessThan(solid.Volume, 0.001))
					{
						list.Add(solid);
					}
				}
			}
			return list;
		}

		private List<Pole2D> GetSolidProfile(Solid solid)
		{
			List<Pole2D> list = new List<Pole2D>();
			foreach (object obj in solid.Faces)
			{
				Face face = (Face)obj;
				if (!(face.GetType() != typeof(PlanarFace)) && Geometry.IsEqual((face as PlanarFace).FaceNormal(), XYZ.BasisZ))
				{
					foreach (object obj2 in face.EdgeLoops)
					{
						EdgeArray edgeArray = (EdgeArray)obj2;
						new List<EdgeInfo>();
						foreach (object obj3 in edgeArray)
						{
							Curve curve = ((Edge)obj3).AsCurve();
							if (!Geometry.LessThan(curve.Length, this.m_MinCurveLength))
							{
								Pole2D item = Pole2D.TransformWallToStructure2DPole(curve, ElementId.InvalidElementId, 0.0, 0.0);
								list.Add(item);
							}
						}
					}
				}
			}
			return list;
		}

		private ExternalCommandData m_Revit;

		private Document m_Doc;

		private double m_MinCurveLength = Common.MMToFeet(0.8);
	}
}
